/*
 * =====================================================================================
 *
 *       Filename:  data2graph.cpp
 *
 *    Description:  implementation of data2graph
 *
 *        Version:  1.0
 *        Created:  08/06/2009 11:08:29 AM
 *       Revision:  none
 *       Compiler:  gcc
 *
 *         Author:  Zhao Zhao
 *        Company:  ndssl
 *
 * =====================================================================================
 */

#include "data2graph.h"
#include <math.h>
#include "../share/utils.hpp"

Data2Graph::Data2Graph(const char* configFile)
{
	cout<<"Initializing module..."<<endl;
	arg = new Arg;
	arg->Configure(configFile);
	
	beginDate = atoi(arg->GetValue("begin_date")[0].c_str());  
	endDate = atoi(arg->GetValue("end_date")[0].c_str());
	cout<<"Initializing module done!"<<endl<<endl<<endl;
}
Data2Graph::~Data2Graph(void)
{
	delete arg;
}


void Data2Graph::Processing()
{
	ReadData(arg->GetValue("data_file")[0].c_str());
	GenerateDataVec();
	CalcCCMatrix();
	GenerateGraph();
}
void Data2Graph::ReadData(const char* fData)
{
	cout<<"Begin reading data from "<<fData<<endl;
	ifstream ifp(fData);
	if(!ifp.is_open())
	{
		cout<<"Cannot open "<<fData<<endl;
		exit(1);
	}
	cout.flush();
	while(1)
	{
		char line[256];
		ifp.getline(line, 256);                 /* read a line */
		if(!ifp.good()) break;
		
		
		vector<string> dataVec = TokenLine(line);                  /* data in the line */

		StockData dataTemp;                     /* stock data, price, volume, etc. */
		dataTemp.open	= atof(dataVec[2].c_str()); 
		dataTemp.high	= atof(dataVec[3].c_str());
		dataTemp.low	= atof(dataVec[4].c_str());
		dataTemp.close	= atof(dataVec[5].c_str());
		dataTemp.volume	= atoi(dataVec[6].c_str());
		stockMap[dataVec[1]][dataVec[0]] = dataTemp;
	}
	cout<<"Reading stock data done."<<endl;
	
	//erase all the stocks whose dates of trading are less than the "TradingDay" days
	short TradingDay = atoi(arg->GetValue("trading_days_larger_than")[0].c_str());
	cout<<"Drop uncompleted data"<<endl;
	StockMap::iterator it, ittmp;
	for(it = stockMap.begin(); it != stockMap.end(); it++)
	{
		ittmp = it;
		if((*it).second.size() < TradingDay)
		{
			it--;
			stockMap.erase(ittmp);
		}
	}
	stockNum = stockMap.size();
	cout<<"There are "<<stockNum<<" stocks whose trading dates are over "<<TradingDay;
	cout<<", which will be selected in our dataset."<<endl;
	
	return;
}
void Data2Graph::GenerateDataVec()
{
	cout<<"Begin generate time series in the range from "<<beginDate<<" to "<<endDate<<endl;
	StockMap::iterator itName;                  /* traverse on all the stock name */
	map<string, StockData>::iterator itDate;    /* traverse on all dates */
	for(itName = stockMap.begin(); itName!=stockMap.end(); itName++)
	{
		for(itDate = (*itName).second.begin(); itDate!=(*itName).second.end(); itDate++)
		{

			int tmpDate = atoi((*itDate).first.c_str());
//			cout<<(*itName).first<<" "<<tmpDate<<endl;
			if(tmpDate > beginDate && tmpDate < endDate)
			{
				float price2 = (*itDate).second.close; /* close price of current day */
				itDate--;
				float price1 = (*itDate).second.close; /* close price of the day before current day */
				itDate++;
				float returnVal = log(price2/price1); /* calculate return */
				stockVec[(*itName).first].push_back(returnVal); /* already sorted by dates */
//				cout<<(*itName).first<<" "<<(*itDate).first<<" "<<price2<<" "<<price1<<endl;
			}
		}
	}
	cout<<"Time series generating done"<<endl;
}

void Data2Graph::CalcCCMatrix()
{
	cout<<"Begin calculate correlation coefficient"<<endl;
	StockVec::iterator it1;
	StockVec::iterator it2;

	for(it1 = stockVec.begin(); it1!= stockVec.end(); it1++)
	{
		//generate triangle, later we will expand it to full
		for(it2 = it1; it2!= stockVec.end(); it2++)
		{
			if(it1 == it2) 
			{
				stockCC[(*it1).first][(*it2).first] = -1; /* real cc should be 1 here, but we let it to be -1 to not build edge between a stock and itself */
				continue;
			}
			stockCC[(*it1).first][(*it2).first] = CalcCC((*it1).second, (*it1).second.size(), (*it2).second, (*it2).second.size());
		}
	}
	//expand to full matrix
	for(it1 = stockVec.begin(); it1!= stockVec.end(); it1++)
	{
		for(it2 = it1; it2!= stockVec.end(); it2++)
		{
			if(it1 == it2) 
			{
				stockCC[(*it1).first][(*it2).first] = -1; /* real cc should be 1 here, but we let it to be -1 to not build edge between a stock and itself */
				continue;
			}
			stockCC[(*it2).first][(*it1).first] = stockCC[(*it1).first][(*it2).first];
		}
	}
	cout<<"CC calculation done"<<endl;
}
void Data2Graph::GenerateGraph()
{
	float thresh = atof(arg->GetValue("CCthreshold")[0].c_str()); /* threshold for CC */
	
	//generate graph named as "stock_20080504_20080907.gph"
	string path(arg->GetValue("graph_name")[0].c_str());
	path.append("_");
	path.append(to_string(beginDate));
	path.append("_");
	path.append(to_string(endDate));
	path.append(".gph");
	
	cout<<"Writing graph to "<<path<<endl;
	cout<<"Two stocks whose CC is larger than "<<thresh<<" will be assigned an edge"<<endl;
	ofstream fGraph(path.c_str());
	if(!fGraph.is_open())
	{
		cout<<"Graph writing error"<<endl;
		exit(1);
	}
	fGraph<<stockNum<<endl;
	CC::iterator it1;
	map<string, float>::iterator it2;
	vector<string> neighbor;
	for(it1 = stockCC.begin(); it1!=stockCC.end(); it1++)
	{
		neighbor.clear();
		for(it2 = (*it1).second.begin(); it2!=(*it1).second.end(); it2++)
		{
			if((*it2).second<thresh) continue;
			neighbor.push_back((*it2).first);
		}
		fGraph<<(*it1).first<<"\t"<<neighbor.size()<<endl;
		for(int i = 0; i<neighbor.size(); i++)
		{
			fGraph<<"\t\t"<<neighbor[i]<<"\t"<<(*it1).second[neighbor[i]]<<"\t"<<"0"<<endl;
		}
	}
	fGraph.close();
	cout<<"Graph generating done"<<endl;
}
vector<string> Data2Graph::TokenLine(char* line)
{
	
	cout<<line<<endl;
	char* str = strtok(line, ", \t");
	cout<<str<<endl;		
	vector<string> strVec;               
	strVec.resize(0);
	while(str != NULL)
	{
		string tmp(str);
		cout<<tmp<<endl;
		strVec.push_back(tmp);
		str = strtok(NULL, ", \t");
	}
	return strVec;	
}


int main(int argc, char* argv[])
{
	if(argc != 2)
	{
		cout << "USAGE: ./data2graph sample.conf" << endl;
		exit(1);
	}
	Data2Graph* data2graph = new Data2Graph(argv[1]);
	data2graph->Processing();

	return 0;
}
